package ast;

import lombok.extern.slf4j.Slf4j;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.parser.Scanner;
import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
import org.eclipse.jdt.internal.formatter.DefaultCodeFormatterOptions;
import org.eclipse.jdt.internal.formatter.Token;
import org.eclipse.jdt.internal.formatter.TokenManager;
import org.junit.Test;
import visitor.pattern.mysonar.DoNotThrowAtFinallyBlockCheck;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;


@Slf4j
public class Demo {

    private String ClassFile = Demo.class.getResource("/ast/tsa/D.java").toString().substring(6);
    private List<File> classpath = Collections.singletonList(new File(ClassFile));
    private final Set<String> JRE_JARS = new HashSet(Arrays.asList("rt.jar", "jrt-fs.jar", "android.jar"));
    private String source;
    private TokenManager tokenManager;

    /**
     * 基于eclipseJDT的静态代码扫描
     * @throws Exception
     */
    @Test
    public void testCheck() throws Exception {
        CompilationUnit astNode = parse();
        next(astNode);
        new DoNotThrowAtFinallyBlockCheck(astNode,tokenManager).doCheck();
    }


    @Test
    public void test1() throws Exception {
        log.debug(ClassFile);
        log.debug(classpath.toString());
        CompilationUnit astNode = parse();
        IProblem[] problems = astNode.getProblems();
        log.debug(problems.toString());
        next(astNode);
    }

    private void next(CompilationUnit astNode) {
        List<Token> tokens = new ArrayList<>();
        Scanner scanner = new Scanner(
                true,
                false,
                false,
                CompilerOptions.versionToJdkLevel("11"),
                null,
                null,
                false
        );
        scanner.fakeInModule = "module-info.java".equals(classpath.get(0).getName());
        scanner.setSource(source.toCharArray());
        while (true) {
            try {
                int tokenType = scanner.getNextToken();
                Token token = Token.fromCurrent(scanner, tokenType);
                tokens.add(token);
                if (tokenType == TerminalTokens.TokenNameEOF) {
                    break;
                }
            } catch (InvalidInputException e) {
                throw new IllegalStateException(e);
            }
        }
        tokenManager = new TokenManager(tokens, source, new DefaultCodeFormatterOptions(new HashMap<>()));
    }

    public CompilationUnit parse() throws Exception {
        ASTParser astParser = astParser();
        astParser.setUnitName(classpath.get(0).getName());
        File file = classpath.get(0);
        source = new String(Files.readAllBytes(Paths.get(file.getPath())));
        astParser.setSource(source.toCharArray());
        CompilationUnit astNode;
        try {
            astNode = (CompilationUnit) astParser.createAST(null);
        } catch (Exception e) {
            log.error("ECJ: Unable to parse file", e);
            throw new RuntimeException("ECJ: Unable to parse file.", e);
        }
        return astNode;
    }

    public ASTParser astParser() {
        ASTParser astParser = ASTParser.newParser(AST.getJLSLatest());
        Map<String, String> options = new HashMap();
        //Setting Compliance Level.
        options.put(JavaCore.COMPILER_COMPLIANCE, Integer.toString(11));//设置java语言版本
        //指定是否使用源代码级别的兼容性
        options.put(JavaCore.COMPILER_SOURCE, Integer.toString(11));
        //每个编译单元报告的最大问题数。
        options.put(JavaCore.COMPILER_PB_MAX_PER_UNIT, "42000");
        //启用对预览语言功能的支持。
        options.put(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, "disable");

        astParser.setCompilerOptions(options);

        List<File> DEFAULT_CLASSPATH = FilesUtils.getClassPath(FilesUtils.DEFAULT_TEST_JARS_DIRECTORY);

        boolean includeRunningVMBootclasspath = DEFAULT_CLASSPATH.stream()
                .noneMatch(f -> JRE_JARS.contains(f.getName()));

        astParser.setEnvironment(DEFAULT_CLASSPATH.stream()
                .map(File::getAbsolutePath)
                .toArray(String[]::new), new String[]{}, new String[]{}, true);

        astParser.setResolveBindings(true);
        astParser.setBindingsRecovery(true);
        return astParser;
    }
}
